package edu.ntnu.superrss.service;

import edu.ntnu.superrss.models.User;
import java.util.List;
import edu.ntnu.superrss.models.Category;
import edu.ntnu.superrss.utils.HibernateUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.stereotype.Service;

/**
 * FeedServiceImpl is an implementation of FeedService. The main
 * objective of this class is to act as a service layer for controler objects.
 * Database calls are placed in this class for persistence, a seperate DAO implementation
 * could also have been used.
 *
 */
@Service
public class CategoryServiceImpl implements CategoryService {

    /**
     * Adds a SyndFeed object to a user
     * <p>
     * This method adds a SyndFeed object to a user and stores it in the database
     * It uses uniqueResult of the Query interface since it expects a single result, an exception will be thrown
     * if more than one match is found
     *
     * @param  url  a String with the feed URL to be added
     * @param  user a String with the name of the user to add the feed to
     */
    public Category addCategory(String username, String title) throws HibernateException, NullPointerException, IllegalArgumentException {
        Logger.getLogger(CategoryServiceImpl.class.getName()).log(Level.INFO, "CategoryService::addCategory(Category " + title);
        Session session = HibernateUtil.getSessionFactory().openSession();
        
        Category category, dbCategory;
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            User user = (User)session.createQuery("from User where username = ?)").setParameter(0, username).uniqueResult();

            dbCategory = (Category) session.createQuery("From Category where title = ?").setParameter(0, title).uniqueResult();
            if (dbCategory != null) {
                category = dbCategory;
            } else {
                category = new Category(title);
                session.save(category);
            }

            user.addCategory(category);
            session.merge(user);
            session.flush();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e; // TODO: LOG4J
        } finally {
            session.close();
        }

        return category;
    }
    
    public Category addCategory(User user, Category category) {
        return this.addCategory(user.getUsername(), category.getTitle());
    }

    /**
     * Returns all Category objects from the user that belongs to the specified User
     *
     * <p>
     *
     * @param  user a String representation of the User, used to identify which SyndFeed objects to get
     * @return  the list with Category elements or null if an error occured
     * @throws  HibernateException
     */
    public List getCategories(String user) throws HibernateException, NullPointerException {

        User properUser;
        List l;

        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            properUser = (User) session.createQuery("from User where username = ?)").setParameter(0, user).uniqueResult();
            l = new ArrayList(properUser.getCategories());

            session.flush();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e; // TODO: LOG4J
        } finally {
            session.close();
        }
        return l;
    }

    public List getCategories(User user) {
        return this.getCategories(user.getUsername());
    }

    public List getCategories(Long[] categoryIds) {
        List categories;

        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            categories = new ArrayList<Category>(session.createQuery("from Category where id in (:categoryIds)").setParameterList("categoryIds", categoryIds).list());

            session.flush();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e; // TODO: LOG4J
        } finally {
            session.close();
        }
        return categories;
    }

    public Category getCategory(Long id) {
        Logger.getLogger(CategoryServiceImpl.class.getName()).log(Level.INFO, "CategoryService::getCategory(Long " + id + ")");

        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        Category category = null;
        try {
            tx = session.beginTransaction();
            category = (Category) session.createQuery("From Category WHERE id = ?").setParameter(0, id).uniqueResult();
            session.flush();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e; // TODO: LOG4J
        } finally {
            session.close();
        }
        return category;
    }

    public void removeCategory(User user, Long id) {
        Logger.getLogger(CategoryServiceImpl.class.getName()).log(Level.INFO, "CategoryService::removeCategory(User " + user.getUsername() + ", " + "Long " + id);

        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            Category category = user.getCategory(id);
            user.removeCategory(category);//burde egentlig kalle en egen UserService
            session.saveOrUpdate(user);

            session.flush();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e; // TODO: LOG4J
        } finally {
            session.close();
        }
    }
}
